import json
import os
import re
import sys
import time
import traceback
from datetime import datetime

import PySimpleGUI as sg
import requests
from selenium import webdriver
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
from selenium.webdriver.edge.service import Service as EdgeService
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.core.download_manager import WDMDownloadManager
from webdriver_manager.core.http import HttpClient
from webdriver_manager.microsoft import EdgeChromiumDriverManager

sg.theme("SystemDefaultForReal")

VERSION = "v0.2.1"

root_path = os.path.dirname(os.path.realpath(sys.argv[0]))
cfg_path = os.path.join(root_path, "config")
log_path = os.path.join(root_path, "log")
save_path = os.path.join(root_path, "save")


ICON = b"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABEVBMVEX////+/v77+/uNjY3BwsH8/Pz9/f3o6OcYGRhPUE9pamoAAQAICAipqqm/v78mJiYwMTAsLCw9PT3x8fGioaLZ2dne3d5eXl5lZWX6+vqVlZUAAADf399+fn7g4ODq6uokJSQ+Pj7y8vLk5OQYGBhycnIEBQSCgoI0NDQJCgm5ubnNzc0REhF/f3+ioqJfX18EBAROTk4DAwNkZGRPT0+9vbzw8PBNTU2UlJSsrKwGBwYfHx9bW1t0dHRtbW2ampqPj4+enp6LjIs8PDwMDAwnKCfp6enz8/MxMTEbHBs4ODhEREQ7OzspKSkrLCwzMzMyMzI/Pz8rKysTFBRubm719fXc3Nza2trY2Njd3d3b29sfGssnAAAAAWJLR0QAiAUdSAAAAAd0SU1FB+cCDgwUOFI8M2YAAAC6SURBVBjTY2CAAEZGBhTACEYIwMTAzMLAiuCzMbBzcAJJOGBl4OLm4QWqg/P5+AUEhYRhxgApfhFRMXEJBkmYiVLSfDIMstJyEGPYGOQVpBWVlFWkxSEWMTGoqkmrMzBoSGtqgcxlZdDWkZbW1WPQlzYQAhnIxmAobWRsYspgZi4tbQEy19LK2sbWzt7B0cnZxdWNgcHdw9PL28PH188/IDAoOISBITRMJjwiLDwyMjJKNDJSDs3TQAAAp/wUa2htV68AAAAldEVYdGRhdGU6Y3JlYXRlADIwMjMtMDItMTRUMTI6MjA6NTUrMDA6MDCtIIa4AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIzLTAyLTE0VDEyOjIwOjU1KzAwOjAw3H0+BAAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyMy0wMi0xNFQxMjoyMDo1NiswMDowMLqABUYAAAAASUVORK5CYII="


SOURCE_URL = "https://gitee.com/haraella/ark-gacha/tree/master"

# 个人中心登录页面
LOGIN_URL = "https://ak.hypergryph.com/user/login"
# 官服获取token
TOKEN_URL = "https://web-api.hypergryph.com/account/info/hg"
# b服获取token
B_TOKEN_URL = "https://web-api.hypergryph.com/account/info/ak-b"
# 获取用户信息
USER_INFO_URL = "https://as.hypergryph.com/u8/user/info/v1/basic"
# 获取寻访记录
GACHA_INFO_URL = "https://ak.hypergryph.com/user/api/inquiry/gacha"


BLUE_BUTTON_COLOR = ("#f1f6f8", "#2869A2")
RED_BUTTON_COLOR = ("#f1f6f8", "#A22828")


class NoChromeException(Exception):
    pass


class NoEdgeException(Exception):
    pass


class NotLoginException(Exception):
    pass


class CustomHttpClient(HttpClient):

    def get(self, url, params=None, **kwargs):
        return requests.get(url, params, **kwargs)


class AkGacha():

    def __init__(self):
        self.browser: str = None
        self.init()

    def init(self):
        self.channel_id: str = None
        self.token: str = None
        self.uid: str = None
        self.nick_name: str = None
        self.gacha_time = 0

    def __edge_driver(self):
        options = webdriver.EdgeOptions()
        options.add_experimental_option('excludeSwitches', ['enable-logging'])
        service = EdgeService(EdgeChromiumDriverManager(
            path=root_path, download_manager=WDMDownloadManager(CustomHttpClient())).install())
        return webdriver.Edge(service=service, options=options)

    def __chrome_driver(self):
        options = webdriver.ChromeOptions()
        options.add_experimental_option('excludeSwitches', ['enable-logging'])
        service = ChromeService(ChromeDriverManager(
            path=root_path, download_manager=WDMDownloadManager(CustomHttpClient())).install())
        return webdriver.Chrome(service=service, options=options)

    def login(self):
        """
        登录用户中心
        """
        self.init()

        if self.browser.lower() == "chrome":
            try:
                driver = self.__chrome_driver()
            except WebDriverException:
                raise NoChromeException("Message: unknown error: cannot find Chrome binary")
        else:
            try:
                driver = self.__edge_driver()
            except WebDriverException:
                raise NoEdgeException("Message: unknown error: cannot find Edge binary")

        driver.get(LOGIN_URL)
        # 等待登录成功
        WebDriverWait(driver, 600, 1).until(EC.visibility_of_element_located((By.XPATH, "//*[text()='角色昵称']")))

        # 如果进入了b服主页
        if "user/bilibili/home" in driver.current_url:
            self.channel_id = "2"
            token = B_TOKEN_URL
        else:
            self.channel_id = "1"
            token = TOKEN_URL

        driver.get(token)
        WebDriverWait(driver, 20, 1).until(EC.url_contains("account/info"))

        response = json.loads(re.findall(r"\{.*\}", driver.page_source)[0])

        if response.get("code") == 0 and response.get("data"):
            self.token = response.get("data").get("content")

            if self.channel_id == "1":
                data = {"appId": 1,
                        "channelMasterId": 1,
                        "channelToken": {"token": self.token}}
            if self.channel_id == "2":
                data = {"token": self.token}

            response = requests.post(USER_INFO_URL, json=data).json()

            if response.get("status") == 0 and response.get("data"):
                self.uid = response.get("data").get("uid")
                self.nick_name = response.get("data").get("nickName")

        else:
            raise NotLoginException("Login Failed")

    def get_gacha_info(self, pool: str, min_ts: int):
        """
        获取抽卡数据

        :params:
            ``pool``: 要统计的卡池名称
            ``min_ts``: 在此时间戳之前的不会统计

        :return: 抽卡信息  {UID, NickName, GachaTime, CharName, Rarity, IsNew}
        """
        if self.token:
            for page in range(1, 11):
                data = {
                    "page": page,
                    "token": self.token,
                    "channelId": self.channel_id
                }
                response = requests.get(GACHA_INFO_URL, params=data).json()
                if response.get("code") == 0 and response.get("data"):
                    gacha_list = response.get("data").get("list")

                    if not gacha_list:
                        return

                    for gacha in gacha_list:
                        self.gacha_time = max(self.gacha_time, gacha.get("ts"))
                        if gacha.get("ts") <= min_ts:
                            return

                        yield from [{
                            "CharName": char.get("name"),
                            "Rarity": char.get("rarity"),
                            "GachaTime": gacha.get("ts"),
                            "IsNew": char.get("isNew")}
                            for char in gacha.get("chars")
                            if gacha.get("pool") == pool]
                else:
                    self.token = None


gacha_info_layout = [
    [sg.Table(headings=["No.", "干员代号", "稀有度", "数量", "new的数量"],
              values=[],
              col_widths=[3, 12, 6, 8, 10],
              auto_size_columns=False,
              justification='right',
              num_rows=22,
              alternating_row_color='lightyellow',
              k='-统计结果-',
              expand_x=True,
              )],
    [sg.Text("总抽数: ", font="微软雅黑 11"), sg.Text(k="-总抽数-", font="微软雅黑 11")],
]

login_layout = [
    [sg.Button("登录", s=(18, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 11")],
    [sg.Text("请先登录", k="-登录结果-", font="微软雅黑 11")],
]

gacha_layout = [
    [sg.Button("统计", s=(18, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 11")],
    [sg.Checkbox("统计", k="-统计勾选框-", enable_events=True, font="微软雅黑 11", pad=(0, 0)),
     sg.Input(default_text="3600", k="-时间-", enable_events=True, disabled=True, size=(8, 1), pad=(0, 0)),
     sg.Text("秒内抽取的干员", font="微软雅黑 11", pad=(0, 0))],
]

ctrl_layout = [
    [sg.Frame("账号登录", layout=login_layout, s=(350, 120), font="微软雅黑 11")],
    [sg.Frame("抽卡统计", layout=gacha_layout, s=(350, 100), font="微软雅黑 11")],
]

home_layout = [
    [sg.Button("新建统计任务", s=(15, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 12")],
    [sg.Button("打开统计任务", s=(15, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 12")],
]

task_layout = [
    [sg.Column(vertical_alignment="top", layout=gacha_info_layout),
     sg.Column(vertical_alignment="top", layout=ctrl_layout)],
]


def main_window():
    layout = [
        [sg.Menu([["文件", ["新建统计任务", "打开统计任务", "选项", "退出"]], ["帮助", ["关于"]]],
                 k="-菜单栏-")],
        [sg.Column(layout=task_layout, k="-任务页面-", visible=False)],
        [sg.Column(layout=home_layout, justification="center", k="-主页-",)],
    ]

    window = sg.Window(f"明日方舟抽卡统计 {VERSION}", layout, size=(700, 500), icon=ICON,
                       element_justification="center", font="微软雅黑 10")
    return window


def goto_task_window():
    window["-主页-"].update(visible=False)
    window["-任务页面-"].update(visible=True)
    window.visibility_changed()


def update_table(save_file):
    rarity_map = {
        5: "六星",
        4: "五星",
        3: "四星",
        2: "三星",
        1: "二星",
        0: "一星",
    }
    total_chars = sum([save_file["GachaData"][uid]["TotalChars"] for uid in save_file["GachaData"]])
    window["-总抽数-"].update(total_chars)
    count_table = [[char,
                    rarity_map[save_file["CharsData"][char]["Rarity"]],
                    save_file["CharsData"][char]["Count"],
                    save_file["CharsData"][char]["NewCount"],
                    save_file["CharsData"][char]["Rarity"]]
                   for char in save_file["CharsData"]]
    count_table = sorted(count_table, key=lambda char: char[2], reverse=True)
    count_table = sorted(count_table, key=lambda char: char[-1], reverse=True)
    count_table = [[index+1]+value[:-1] for index, value in enumerate(count_table)]
    window["-统计结果-"].update(count_table)
    window.visibility_changed()


def add_task_window():
    layout = [
        [sg.Text("任务名称:"), sg.Input(k="-任务名称-", size=(30, 1), enable_events=True)],
        [sg.Text("卡池名称:"), sg.Input(k="-卡池名称-", size=(30, 1), enable_events=True)],
        [sg.Button("确认", s=(15, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 10"),
         sg.Button("取消", s=(15, 1), button_color=RED_BUTTON_COLOR, font="微软雅黑 10"),],
    ]
    add_task_window = sg.Window("新建统计任务", layout, modal=True, icon=ICON, font="微软雅黑 10")

    while True:
        event, values = add_task_window.read()

        if event in (sg.WIN_CLOSED, "取消"):
            break
        if event == "-任务名称-":
            add_task_window["-任务名称-"].update(background_color="white")
        if event == "-卡池名称-":
            add_task_window["-卡池名称-"].update(background_color="white")
        if event == "确认":
            if not values["-任务名称-"]:
                add_task_window["-任务名称-"].update(background_color="#FCAFAF")
                continue
            if not values["-卡池名称-"]:
                add_task_window["-卡池名称-"].update(background_color="#FCAFAF")
                continue

            filename = f'{values["-任务名称-"]}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'

            global save_file
            save_file = sg.UserSettings(filename=filename, path=save_path)
            save_file["Pool"] = values["-卡池名称-"]
            save_file["GachaData"] = {}
            save_file["CharsData"] = {}

            goto_task_window()
            update_table(save_file)
            break

    add_task_window.close()


def option_window():
    option_layout = [
        [sg.Text("浏览器选择:"), sg.OptionMenu(values=["Edge", "Chrome"], default_value=cfg_file["Browser"], k="-浏览器选择-", size=(30, 1))],
    ]
    layout = [
        [sg.Column(layout=option_layout, size=(500, 100))],
        [sg.Button("确认", s=(15, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 10"),
         sg.Button("取消", s=(15, 1), button_color=RED_BUTTON_COLOR, font="微软雅黑 10"),
         sg.Button("应用", s=(15, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 10"),],
    ]
    option_window = sg.Window("选项", layout, modal=True, icon=ICON, font="微软雅黑 10")
    while True:
        event, values = option_window.read()

        if event in (sg.WIN_CLOSED, "取消"):
            break
        if event == "确认":
            cfg_file["Browser"] = values["-浏览器选择-"]
            break
        if event == "应用":
            cfg_file["Browser"] = values["-浏览器选择-"]

    option_window.close()


def about_window():
    about_layout = [
        [sg.Text("明日方舟抽卡统计工具", font="微软雅黑 12"),],
        [sg.Text("开发者: Haraella"),],
        [sg.Text("邮箱: Haraella@qq.com"),],
        [sg.Text("bilibili用户名: bili_6117543681"),],
        [sg.Text(f"版本: {VERSION}"),],
    ]
    layout = [
        [sg.Column(layout=about_layout, size=(300, 200))],
        [sg.Button("主页", s=(15, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 10"),
         sg.Button("确认", s=(15, 1), button_color=BLUE_BUTTON_COLOR, font="微软雅黑 10"),],
    ]
    about_window = sg.Window("关于", layout, modal=True, icon=ICON, font="微软雅黑 10")
    while True:
        event, values = about_window.read()

        if event in (sg.WIN_CLOSED, "确认"):
            break
        if event == "主页":
            sg.webbrowser.open(SOURCE_URL)

    about_window.close()


if __name__ == "__main__":
    cfg_file = sg.UserSettings(path=cfg_path)
    if not cfg_file["Browser"]:
        cfg_file["Browser"] = "Edge"

    akgacha = AkGacha()
    akgacha.browser = cfg_file["Browser"]

    window = main_window()
    while True:
        event, values = window.read()
        print(event)
        print(values)
        print(save_path)
        print(cfg_file)
        if event in (sg.WIN_CLOSED, "退出"):
            break
        if event == "新建统计任务":
            akgacha.init()
            add_task_window()
        if event == "打开统计任务":
            akgacha.init()
            filename = sg.filedialog.askopenfilename(
                initialdir=save_path, filetypes=(("Json Files", "*.json"),))
            if filename:
                save_file = sg.UserSettings(filename=filename)
                goto_task_window()
                update_table(save_file)
        if event == "选项":
            option_window()
        if event == "关于":
            about_window()
        if event == "登录":
            try:
                akgacha.browser = cfg_file["Browser"]
                akgacha.login()
            except NoChromeException:
                window["-登录结果-"].update("未安装Chrome浏览器")
            except NoEdgeException:
                window["-登录结果-"].update("未安装Edge浏览器")
            except Exception:
                window["-登录结果-"].update("登录失败")
            else:
                window["-登录结果-"].update(f"当前用户:\n{akgacha.nick_name}")
            window.visibility_changed()

        if event == "-时间-" and values['-时间-']:
            try:
                float(values['-时间-'])
            except:
                if not len(values['-时间-']) == 0:
                    window['-时间-'].update(values['-时间-'][:-1])

            if len(values['-时间-']) > 7:
                window['-时间-'].update(values['-时间-'][:-1])

        if event == "-统计勾选框-" and values["-统计勾选框-"]:
            window['-时间-'].update(disabled=False)
        if event == "-统计勾选框-" and not values["-统计勾选框-"]:
            window['-时间-'].update(disabled=True)

        if event == "统计":
            try:
                if akgacha.uid:
                    if akgacha.uid not in save_file["GachaData"]:
                        save_file["GachaData"][akgacha.uid] = {
                            "NickName": akgacha.nick_name,
                            "GachaChars": [],
                            "TotalChars": 0,
                            "LastGachaTime": akgacha.gacha_time
                        }
                        save_file.save()
                    else:
                        akgacha.gacha_time = save_file["GachaData"][akgacha.uid]["LastGachaTime"]

                    pool = save_file["Pool"]
                    times = values["-时间-"] if values["-时间-"] and values["-统计勾选框-"] else 2592000
                    print(times)
                    min_ts = max(akgacha.gacha_time, int(time.time()-float(times)))
                    gacha_info = list(akgacha.get_gacha_info(pool, min_ts))
                    save_file["GachaData"][akgacha.uid]["GachaChars"] += gacha_info
                    save_file["GachaData"][akgacha.uid]["TotalChars"] += len(gacha_info)
                    save_file["GachaData"][akgacha.uid]["LastGachaTime"] = akgacha.gacha_time

                    print(f"gacha_info: {gacha_info}")
                    for char in gacha_info:
                        if char["CharName"] not in save_file["CharsData"]:
                            save_file["CharsData"][char["CharName"]] = {"Count": 0, "NewCount": 0, "Rarity": char["Rarity"]}

                        save_file["CharsData"][char["CharName"]]["Count"] += 1
                        if char["IsNew"]:
                            save_file["CharsData"][char["CharName"]]["NewCount"] += 1

                    save_file.save()
                    update_table(save_file)
                else:
                    window["-登录结果-"].update("请先登录")
                    window.visibility_changed()
            except:
                print(traceback.format_exc())

    window.close()
